package sys

import (
	"fmt"
	"runtime"
	"time"

	"vov.cx/lib/utils"
)

var (
	startTime = time.Now()
	timeMap   = map[string]string{
		"second":  "second",
		"seconds": "seconds",
		"minute":  "minute",
		"minutes": "minutes",
		"hour":    "hour",
		"hours":   "hours",
		"day":     "day",
		"days":    "days",
		"week":    "week",
		"weeks":   "weeks",
		"month":   "month",
		"months":  "months",
		"year":    "year",
		"years":   "years",
	}
)

type AppStatus struct {
	Uptime       string `json:"uptime,omitempty"`
	NumGoroutine int    `json:"num_goroutine,omitempty"`

	// General statistics.
	MemAllocated string `json:"mem_allocated"` // bytes allocated and still in use
	MemTotal     string `json:"mem_total"`     // bytes allocated (even if freed)
	MemSys       string `json:"mem_sys"`       // bytes obtained from system (sum of XxxSys below)
	Lookups      uint64 `json:"lookups"`       // number of pointer lookups
	MemMallocs   uint64 `json:"mem_mallocs"`   // number of malloc
	MemFrees     uint64 `json:"mem_frees"`     // number of frees

	// Main allocation heap statistics.
	HeapAlloc    string `json:"heap_alloc"`    // bytes allocated and still in use
	HeapSys      string `json:"heap_sys"`      // bytes obtained from system
	HeapIdle     string `json:"heap_idle"`     // bytes in idle spans
	HeapInuse    string `json:"heap_inuse"`    // bytes in non-idle span
	HeapReleased string `json:"heap_released"` // bytes released to the OS
	HeapObjects  uint64 `json:"heap_objects"`  // total number of allocated objects

	// Low-level fixed-size structure allocator statistics.
	//	Inuse is bytes used now.
	//	Sys is bytes obtained from system.
	StackInuse  string `json:"stack_inuse"` // bootstrap stacks
	StackSys    string `json:"stack_sys"`
	MSpanInuse  string `json:"m_span_inuse"` // mspan structures
	MSpanSys    string `json:"m_span_sys"`
	MCacheInuse string `json:"m_cache_inuse"` // mcache structures
	MCacheSys   string `json:"m_cache_sys"`
	BuckHashSys string `json:"buck_hash_sys"` // profiling bucket hash table
	GCSys       string `json:"gc_sys"`        // GC metadata
	OtherSys    string `json:"other_sys"`     // other system allocations

	// Garbage collector statistics.
	NextGC       string `json:"next_gc"` // next run in HeapAlloc time (bytes)
	LastGC       string `json:"last_gc"` // last run in absolute time (ns)
	PauseTotalNs string `json:"pause_total_ns"`
	PauseNs      string `json:"pause_ns"` // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256]
	NumGC        uint32 `json:"num_gc"`
}

func GetAppStatus() AppStatus {
	var app AppStatus
	app.Uptime = utils.TimeSincePro(startTime, timeMap)

	m := new(runtime.MemStats)
	runtime.ReadMemStats(m)
	app.NumGoroutine = runtime.NumGoroutine()

	app.MemAllocated = utils.FileSize(m.Alloc)
	app.MemTotal = utils.FileSize(m.TotalAlloc)
	app.MemSys = utils.FileSize(m.Sys)
	app.Lookups = m.Lookups
	app.MemMallocs = m.Mallocs
	app.MemFrees = m.Frees

	app.HeapAlloc = utils.FileSize(m.HeapAlloc)
	app.HeapSys = utils.FileSize(m.HeapSys)
	app.HeapIdle = utils.FileSize(m.HeapIdle)
	app.HeapInuse = utils.FileSize(m.HeapInuse)
	app.HeapReleased = utils.FileSize(m.HeapReleased)
	app.HeapObjects = m.HeapObjects

	app.StackInuse = utils.FileSize(m.StackInuse)
	app.StackSys = utils.FileSize(m.StackSys)
	app.MSpanInuse = utils.FileSize(m.MSpanInuse)
	app.MSpanSys = utils.FileSize(m.MSpanSys)
	app.MCacheInuse = utils.FileSize(m.MCacheInuse)
	app.MCacheSys = utils.FileSize(m.MCacheSys)
	app.BuckHashSys = utils.FileSize(m.BuckHashSys)
	app.GCSys = utils.FileSize(m.GCSys)
	app.OtherSys = utils.FileSize(m.OtherSys)

	app.NextGC = utils.FileSize(m.NextGC)
	app.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000)
	app.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000)
	app.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000)
	app.NumGC = m.NumGC

	return app
}
